package name.jasonfrank;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.Toast;

public class CamCapture2Activity extends Activity 
                                    implements OnClickListener {
	
	protected static final String TAG = "CamCapture";
	protected static final int CAM_PIC_REQUEST_CODE = 1492;
	protected static final String PIC_BASE_NAME = "shareView_";
	protected static final String PIC_EXTENSION = ".jpg";
	protected static final String OUTPUT_DIR_NAME = "DCIM"   + File.separatorChar 
	                                               +"Camera" + File.separatorChar
	                                               +"ShareView";
	// Timestamp pattern to mirror how my phone sets it on filenames. Like: 2011-03-21_21-09-13_221
	//  (the last 3 digits are milliseconds)
	protected static final String TIME_STAMP_PATTERN = "yyyy-MM-dd_HH-mm-ss_S";
	protected String latestPicFullPath;
	protected File   latestPicFileObj;
	protected String latestPicResizedFullPath;
	//protected File   latestPicResizedFileObj;
	protected Bitmap latestPicBitmapResized;
	// The max dimension of any side of an image that we will upload to the server
	// The downsampling algorithm will scale the bitmap image down in powers of 2 so
	//  the no side of the image will have more than this number of pixels.
	protected static final int IMAGE_MAX_SIDE_PIXELS = 1024;
	
	private String emailTo   = "shareview972@gmail.com";
	private String emailFrom = "shareview972@gmail.com";
	private String emailPass = "somepassword";
	
	private Intent camIntent;
	private ProgressDialog progDialog;
	private int maxBarValue = 200;

  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    // Add click listener for the take pic btn
    View takePicBtn = findViewById(R.id.takePicBtn);
    takePicBtn.setOnClickListener(this);
      
  } ////////////// end onCreate() //////////////////

	@Override
	public void onClick(View v) {
		switch(v.getId()) {
			// TAKE PIC BUTTON handler
			//   1. Construct the filename for the new pic about to be taken, then
			//   2. Pass the constructed filename to a Camera intent to take the pic
			case R.id.takePicBtn:
				Log.i("Button", "'Take pic' button pressed.");
				
				// Construct the filename. Use a base (i.e, "shareView_"), plus timestamp.
				String timeStampStr = new SimpleDateFormat(TIME_STAMP_PATTERN).format(new Date());
				String picFilename = PIC_BASE_NAME + timeStampStr + PIC_EXTENSION;
				
				// Deal with the OUTPUT DIRECTORY. Use a seperate dir for our files.
				//  Note that I could not use getExternalStoragePublicDirectory(), as that seems to 
				//  assume that the phone has an external sD card.
				
				// Note that the Android docs explain that the "external" in this method does not 
				//  necessarily refer to storage external to the phone (SD card), but rather storage 
				//  that is distinct from the protected internal storage. See: http://developer.android.com/reference/android/os/Environment.html
				File outDirObj = new File(Environment.getExternalStorageDirectory(), OUTPUT_DIR_NAME);
		    // Create the storage directory if it does not exist
		    if (! outDirObj.exists()){
	        if (! outDirObj.mkdirs()){
	        	String msg = "Failed to create directory \"" + outDirObj.getAbsolutePath() + "\"." +
 		                      "\n Will not try to take picture, etc.";
	          Log.e(TAG, msg);
	          Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
	          return;
	        }
		    }
		    // Get a full absolute path and corresponding File object
		    this.latestPicFullPath = outDirObj.getAbsolutePath() + File.separatorChar + picFilename;
		    this.latestPicFileObj = new File(this.latestPicFullPath);
				
				// Launch the camera to take and return an image
				this.camIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
				Uri picUri = Uri.fromFile(this.latestPicFileObj);
				camIntent.putExtra(MediaStore.EXTRA_OUTPUT, picUri);
				
				startActivityForResult(camIntent, CAM_PIC_REQUEST_CODE );
				break;
		}
	} //////////// end onClick() ////////////////////////
	
	/**
	 * Handle the return of intents that this Activity launched
	 */
	protected void onActivityResult(int requestCode, int resultCode, Intent retDataIntent) {
		// Handle the return of the Camera intent:
		if(requestCode == this.CAM_PIC_REQUEST_CODE) {
			if(resultCode == RESULT_OK ){
				String msg = "Camera app activity returned with RESULT_OK";

						// From: http://mobile.tutsplus.com/tutorials/android/android-sdk-quick-tip-launching-the-camera/
						// "The image that is returned is appropriate for display on a small 
						//  device screen. It comes in directly to the results as an Android Bitmap object."
						//	Bitmap thumbnail = (Bitmap) retDataIntent.getExtras().get("data");
				ImageView imageView = (ImageView) findViewById(R.id.photoResultView);  
						//	imageView.setImageBitmap(thumbnail);  
				
				// Get a bitmap from the latest pic taken filename
						// Bitmap latestPicBitmap = BitmapFactory.decodeFile(this.latestPicFullPath);
				this.latestPicBitmapResized = myDecodeFile(this.latestPicFileObj);  
				
						// imageView.setImageBitmap(latestPicBitmap);
				imageView.setImageBitmap(this.latestPicBitmapResized);
				
				// Prepare and write the resized bitmap to a file, so I can add it as an email attachment, etc
				String resizedPicFullPath = this.latestPicFullPath;
				String strPattern = "(.+)\\" + PIC_EXTENSION + "$";
				String replacePattern = "$1_resized" + PIC_EXTENSION;
				this.latestPicResizedFullPath = resizedPicFullPath.replaceAll(strPattern, replacePattern);
						// Log.i(TAG, "resizedPicFullPath: " + this.latestPicResizedFullPath);
				try {
		       FileOutputStream outStream = new FileOutputStream(this.latestPicResizedFullPath);
		       int compressRate = 90;
		       // Write the resized image to file
		       this.latestPicBitmapResized.compress(Bitmap.CompressFormat.JPEG , 
		      		                                  compressRate, outStream );
		       outStream.flush();
		       outStream.close();
				} catch (Exception e) {
				   e.printStackTrace();
				}								
				
				//if(latestPicBitmap == null ) {
				if(latestPicBitmapResized == null ) {
					msg = "latestPicBitmap IS NULL !!!!!!!!!!!!";
					Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
					Log.i(TAG, msg);
				}
				// Send the email in a background thread
				String progressMsgs = "Thread is sending email...";
				new sendEmailInBackground(this.latestPicResizedFullPath).execute(progressMsgs);
			}
			// Handle cases where "the activity explicitly returned RESULT_CANCELED, 
			//  or didn't return any result, or crashed during its operation".
			else if(resultCode == RESULT_CANCELED ) {
				//String msg = "Camera app activity failed or was cancelled.";
				//Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
			}
			else {
				//Toast.makeText(this, "Unknown result code returned from camera app", Toast.LENGTH_SHORT).show();
			}
		} // end handling camera pic request return		
	} ////////////// end onActivityResult() ////////////////
  

	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		
		//outState.put
	}
	
	protected void onRestoreInstanceState(Bundle inState) {
		super.onRestoreInstanceState(inState);
		
	}
	
	/**
	 * From Thomas Vervest's answer at: http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue
	 * @param f
	 * @return
	 */
	protected Bitmap myDecodeFile(File f){
    Bitmap b = null;
    try {
        //Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        BitmapFactory.decodeStream(fis, null, o);
        fis.close();

        int scale = 1;
        if (o.outHeight > IMAGE_MAX_SIDE_PIXELS || o.outWidth > IMAGE_MAX_SIDE_PIXELS) {
            scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIDE_PIXELS / 
            		                         (double) Math.max(o.outHeight, o.outWidth)) / 
            		                         Math.log(0.5)));
        }

        //Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        // TEMP: hard coding this right now!
        //o2.inSampleSize = 4; 
        fis = new FileInputStream(f);
        b = BitmapFactory.decodeStream(fis, null, o2);
        fis.close();
    } catch (IOException e) {
    	Log.e(TAG, "Problem using BitmapFactory... Here's the stack trace: ", e);
    }
    return b;
	} ///////////// end myDecodeFile() ///////////////////////
	
	
	
	/** Method to create a progress bar dialog of either spinner or horizontal type.  We will
	    only use the spinner (indeterminate progress) type here.
	*/
  @Override
  protected Dialog onCreateDialog(int id) {
    switch(id) {
      case 0:                      // Spinner
          progDialog = new ProgressDialog(this);
          progDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
          progDialog.setMessage("Sending email...");
          return progDialog;
      case 1:                      // Horizontal
          progDialog = new ProgressDialog(this);
          progDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
          progDialog.setMax(maxBarValue);
          progDialog.setMessage("Sending email...:");
          return progDialog;
      default:
      		return null;
    }
  } /////////////// end onCreateDialog() ////////////////////
  
  /** Subclass AsyncTask to prepare and send an email with possible attached image on a 
      background thread.  The three argument types
      <Params, Progress, Result> are (1) a type for the input parameters (String in this case),  (2) a 
      type for any published progress during the background task (String in this case), and (3) a type
      for the objects returned from the background task (if any; in this case it is a Bitmap).  Each of
      these three arguments stands for an array of the corresponding type, so it is possible
      to pass multiple arguments of each kind. If one of the argument types is not needed, specify 
      it by Void.  
  */
	private class sendEmailInBackground extends AsyncTask <String, String, String> {
	
		protected String attachmentFullPath = null;
		protected Mail m;
		
		/**
		 * CONSTRUCTOR.  Used to initialize a possible email attachment path. I need to use this because
		 *  onPreExecute() will not take any parameters and I might need to interact with the user via
		 *  a view before actually sending the email (and you can't interact with a view in doInBackground()).
		 * @param attachmentFullPath
		 */
		public sendEmailInBackground(String attachmentFullPath) {
			super();
			this.attachmentFullPath = attachmentFullPath;
		}
		
		public void setAttachementFullPath(String attachmentFullPath) {
			this.attachmentFullPath = attachmentFullPath;
		}
		
		/** 
		 Prepare the email before trying to send it. Preparations are done here since we may
		 need to interact with the user via a view at this stage.
		 
		 Executes on the main UI thread before the thread run by doInBackground does.  Since
		 it executes on the main UI thread we are free to interact with views on the main
		 thread from this method. This is the place to do any setup required before the task on
		 the background thread runs.  In this example, we use it to launch a progress dialog
		 that will indicate to the user that work is being done while the background task is running.
		*/
		@Override
		protected void onPreExecute () {
					// Call method to create a progress dialog defined by onCreateDialog().  The argument
					// 0 specifies that the progress dialog is indeterminate (indicates that progress is 
					// being made, but not the amount).
					//showDialog(0); 
			
			// Get a reference to the outer class's instance for more elegant code below
			CamCapture2Activity outerClass = CamCapture2Activity.this;
			
			m = new Mail(/* username: */ outerClass.emailTo,outerClass.emailPass); 
			String[] toArr = {outerClass.emailTo}; 
	    m.setTo(toArr); 
	    m.setFrom(outerClass.emailFrom); 
	    m.setSubject("shareview auto sent - by background thread"); 
	    m.setBody("Sent using a subclass that extends AsyncTask");
			
		} //////////// end onPreExecute() ////////////
		
		/** Executes a task on a background thread that is managed automatically by the sysem.
		 Note: since this is a background  thread, we are strictly forbidden to touch any views on
		 the main UI thread directly from this method. */
		@Override
		protected String doInBackground(String... progressMsgs) {
			
			// Optional: publish progress. This will cause onProgressUpdate to run on the main UI thread,
			// with the argument of publishProgress passed to it.
			publishProgress(progressMsgs[0]);
			
			try { 
	    	if(this.attachmentFullPath != null) {
	    		m.addAttachment(this.attachmentFullPath);
	    	} 

	      if(m.send()) { 
	        //Toast.makeText(this, "Email was sent successfully.", Toast.LENGTH_LONG).show(); 
	        //Log.i(TAG, "Email was sent successfully.");
	        return ("Email was sent successfully."); // returns it to onPostExecute()
	      } 
	      else { 
	        //Toast.makeText(this, "Email was NOT sent.", Toast.LENGTH_LONG).show(); 
	        //Log.e(TAG, "Email was NOT sent."); 
	        return("Email was NOT sent."); // returns it to onPostExecute()
	      } 
	    } catch(Exception e) { 
	      //Toast.makeText(this, "There was a problem sending the email.", Toast.LENGTH_LONG).show(); 
	      Log.e(TAG, "Error: Could not send email.", e); 
	      return("Error: Exception raised. Could not send email."); // returns it to onPostExecute()
	    } 
		} ///////////// end doInBackground() ///////////////
		
		/** Override onProgressUpdate to publish progress while the background thread is still running.
		 This runs on main UI thread after publishProgress method invoked on background thread in
		 doInBackground.  Here we do something fairly trivial by sending to the logcat stream a
		 message indicating that we have started processing on the background thread and
		 the url it is using.  A more common real-life application of this method would be to use say 
		 onProgressUpdate(Integer ... prog) to update a progress bar with the fraction of
		 the job completed. (Then you would have to change the AsyncTask parameter pattern for this
		 example to <String, Integer, Bitmap> and change the publishProgress method in 
		 doInBackground accordingly.)  Note that this method runs on the main UI thread so it is 
		 permitted to interact directly with the views there.
		*/
		@Override
		protected void onProgressUpdate(String... progress) {
			
			CamCapture2Activity outerClass = CamCapture2Activity.this;
			Log.i(TAG, "---onProgressUpdate---");
			Log.i(TAG, progress[0]);
			Toast.makeText(outerClass, progress[0], Toast.LENGTH_LONG).show(); 
		}
		
		/** Executed after the thread run by doInBackground has returned. The param
		 passed is the value returned by doInBackground.  This method executes on
		 the main UI thread, so we are free to interact with views on the main thread from 
		 here.
		*/
		@Override
		protected void onPostExecute(String sentEmailResultStr){
					// Stop the progress dialog
					// dismissDialog(0);
			Log.i(TAG, "Sending Email Thread finished.");
			Log.i(TAG, sentEmailResultStr);			
			CamCapture2Activity outerClass = CamCapture2Activity.this;
			Toast.makeText(outerClass, sentEmailResultStr, Toast.LENGTH_LONG).show();
		}
		
	} //////// end inner class BackgroundLoad //////////////
} ////////////////////////// end class //////////////////////